package com.example.activity.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.example.activity.constant.LocalConstant;
import com.example.activity.exception.BizException;
import com.example.activity.po.FileInfoPO;
import com.example.activity.service.*;
import com.example.activity.utils.FileUploadUtil;
import com.example.activity.vo.upload.FileUpDowmInfoVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.file.*;
import java.util.ArrayList;
import java.util.List;

@Service
@Slf4j
public class FileUpLoadServiceImpl implements FileUpLoadService {

    @Resource
    private FileInfoService fileInfoService;

    @Resource
    private FileInfoRelService fileInfoRelService;

    /**
     * 文件上传
     *
     * @param fileList MultipartFile
     */
    @Override
    public List<FileUpDowmInfoVO> uploadFile(List<MultipartFile> fileList) throws Exception {
        // 1.参数校验
        for (MultipartFile file : fileList) {
            checkFileType(file);
        }

        List<FileInfoPO> fileInfoPOList = new ArrayList<>();
        // 2.上传文件到指定路径
        for (MultipartFile file : fileList) {
            String uploadPath = FileUploadUtil.getUploadPath();
            log.info("上传的相对路径:{}", uploadPath);

            Path fullPath = Paths.get(LocalConstant.BASE_ACTIVITI_UPLOAD, uploadPath);

            if (!Files.exists(fullPath)) {
                // 创建目录
                Files.createDirectories(fullPath);
            }

            String originalFilename = file.getOriginalFilename();
            Path fileNameFullPath = Paths.get(fullPath.toString(), originalFilename);


            FileInfoPO fileInfoPO = new FileInfoPO();
            String fileType = FileUploadUtil.getFileType(originalFilename);
            fileInfoPO.setFileType(fileType);
            fileInfoPO.setFileName(originalFilename);
            fileInfoPO.setFilePath(uploadPath);
            fileInfoPOList.add(fileInfoPO);

            // 文件写入
            actualFileWrite(file, fileNameFullPath.toFile());
        }

        fileInfoService.saveBatch(fileInfoPOList);

        // 3.封装响应参数
        List<FileUpDowmInfoVO> fileUploadVOList = new ArrayList<>();
        for (FileInfoPO fileInfoPO : fileInfoPOList) {
            FileUpDowmInfoVO uploadVO = new FileUpDowmInfoVO();
            uploadVO.setId(fileInfoPO.getId());
            uploadVO.setFileName(fileInfoPO.getFileName());

            fileUploadVOList.add(uploadVO);
        }

        log.info("上传文件成功:{}", JSONUtil.toJsonStr(fileUploadVOList));
        return fileUploadVOList;
    }

    public void actualFileWrite(MultipartFile multipartFile, File file) throws IOException {
        FileOutputStream os = null;
        try {
            byte[] bytes = multipartFile.getBytes();
            // 这里用BufferOutputStream，因为文件都是一次性上传的，没有巨大意义。
            os = new FileOutputStream(file);
            os.write(bytes);
            os.flush();
        } catch (IOException e) {
            log.error("actualFileWrite err ", e);
            throw e;
        } finally {
            if (os != null) {
                os.close();
            }
        }
    }


    /**
     * 检查文件类型
     *
     * @param file
     */
    private void checkFileType(MultipartFile file) {
        String originalFilename = file.getOriginalFilename();
        if (StrUtil.isBlank(originalFilename)) {
            throw new BizException("缺少文件名");
        }

        String fileType = FileUploadUtil.getFileType(originalFilename);
        if (StrUtil.isBlank(fileType)) {
            throw new BizException("文件类型异常");
        }

        if (!LocalConstant.BASE_UPLOAD_TYPE_LIST.contains(fileType.toLowerCase())) {
            log.error("上传的文件类型不允许:" + fileType);
            throw new BizException("上传的文件类型不允许:" + fileType);
        }
    }

    /**
     * 下载文件流
     *
     * @param filePath 文件路径
     * @return InputStream
     */
    @Override
    public InputStream downloadFileStream(String filePath) {
        log.info("下载文件流路径:{}", filePath);
        InputStream inputStream = null;
        try {
            inputStream = Files.newInputStream(Paths.get(filePath));
        } catch (IOException e) {
            log.error("下载文件失败", e);
            throw new BizException("下载文件失败");
        }
        return inputStream;
    }

    /**
     * 下载文件
     * @param fileName 文件名
     * @param downloadType 下载类型
     * @param inputStream 输入流
     * @param response 响应流
     */
    public void downStream(String fileName, String downloadType,
                           InputStream inputStream, HttpServletResponse response) throws IOException {

        String downloadMode = downloadType + "; filename=" + "\"" + fileName + "\"";
        response.setContentType("application/octet-stream");
        response.addHeader("Content-Disposition", downloadMode);

        // 100kb
        byte buffer[] = new byte[1024 * 100];
        int length = 0;
        ServletOutputStream outputStream = null;
        try {
            outputStream = response.getOutputStream();
            while ((length = inputStream.read(buffer)) > 0) {
                outputStream.write(buffer, 0, length);
            }
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    log.error("outputStream关闭流异常", e);
                }
            }

            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    log.error("inputStream关闭流异常", e);
                }
            }
        }
    }

}
